﻿using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace CoreHelper
{
    /// <summary>
    /// 基于MemoryCache的缓存辅助类,可用于Winform桌面程序
    /// </summary>
    public static class CacheHelper
    {
        private static readonly Object _locker = new object();//锁
        private static IMemoryCache Cache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new MemoryCacheOptions());

        #region 获取数据缓存,最常用
        /// <summary>
        /// 获取数据缓存
        /// </summary>
        /// <param name="CacheKey">缓存名称</param>
        public static object GetCache(string CacheKey)
        {
            return Cache.Get(CacheKey);
        }

        /*
         * slidingExpiration：用于设置可调过期时间，它表示当离最后访问超过某个时间段后就过期，类型为System.TimeSpan
         * 调用方法1：
         * var 缓存对象 = CoreHelper.MemoryCacheHelper.GetCacheItem<Model.CangFangList>("缓存名称", () => new BLL.CangFangList().Single(CangFangId), new TimeSpan(0, 1, 0));
         * 调用方法2：
         * var 缓存对象 = CoreHelper.MemoryCacheHelper.GetCacheItem<Model.CangFangList>("缓存名称", delegate() { return new BLL.CangFangList().Single(CangFangId); }, new TimeSpan(0, 1, 0));
         */
        /// <summary>
        /// 获取数据缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">缓存名称</param>
        /// <param name="cachePopulate">
        /// lamda表达式:可以是() => new BLL.CangFangList().Single(CangFangId)或者() => "abc"
        /// 或者委托(注意返回)： delegate() { return new BLL.CangFangList().Single(CangFangId); }
        /// </param>
        /// <param name="slidingExpiration">用于设置可调过期时间，它表示当离最后访问超过某个时间段后就过期new TimeSpan(0, 10, 0)或者TimeSpan.FromMinutes(60)</param>
        /// <returns></returns>
        public static T GetCache<T>(String key, Func<T> cachePopulate, TimeSpan slidingExpiration)
        {

            if (String.IsNullOrWhiteSpace(key)) throw new ArgumentException("无效的缓存键");
            if (cachePopulate == null) throw new ArgumentNullException("需要缓存填充");

            if (Cache.Get(key) == null)
            {
                lock (_locker)
                {
                    Cache.Set(key, cachePopulate(), new MemoryCacheEntryOptions()
                    .SetSlidingExpiration(slidingExpiration));
                }
            }
            return (T)Cache.Get(key);
        }

        /*
         * absoluteExpiration：用于设置绝对过期时间，它表示只要时间一到就过期，类型为System.DateTime
         * 调用方法1：
         * var 缓存对象 = CoreHelper.MemoryCacheHelper.GetCacheItem<Model.CangFangList>("缓存名称", () => new BLL.CangFangList().Single(CangFangId), System.DateTime.Now.AddMinutes(20));
         * 调用方法2：
         * var 缓存对象 = CoreHelper.MemoryCacheHelper.GetCacheItem<Model.CangFangList>("缓存名称", delegate() { return new BLL.CangFangList().Single(CangFangId); }, System.DateTime.Now.AddMinutes(20));
         */
        /// <summary>
        /// 获取数据缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">缓存名称</param>
        /// <param name="cachePopulate">
        /// lamda表达式:可以是() => new BLL.CangFangList().Single(CangFangId)或者() => "abc"
        /// 或者委托(注意返回)： delegate() { return new BLL.CangFangList().Single(CangFangId); }
        /// </param>
        /// <param name="absoluteExpiration">用于设置绝对过期时间,System.DateTime.Now.AddMinutes(20)</param>
        /// <returns></returns>
        public static T GetCache<T>(String key, Func<T> cachePopulate, DateTime absoluteExpiration)
        {

            if (String.IsNullOrWhiteSpace(key)) throw new ArgumentException("无效的缓存键");
            if (cachePopulate == null) throw new ArgumentNullException("需要缓存填充");

            if (Cache.Get(key) == null)
            {
                lock (_locker)
                {
                    Cache.Set(key, cachePopulate(), new MemoryCacheEntryOptions()
                    .SetAbsoluteExpiration(absoluteExpiration));
                }
            }
            return (T)Cache.Get(key);
        }
        #endregion

        #region 获取缓存
        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public static T Get<T>(string key) where T : class
        {
            if (key == null)
                throw new ArgumentNullException(nameof(key));

            return Cache.Get(key) as T;
        }

        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public static object Get(string key)
        {
            if (key == null)
                throw new ArgumentNullException(nameof(key));

            return Cache.Get(key);
        }

        /// <summary>
        /// 获取缓存集合
        /// </summary>
        /// <param name="keys">缓存Key集合</param>
        /// <returns></returns>
        public static IDictionary<string, object> GetAll(IEnumerable<string> keys)
        {
            if (keys == null)
                throw new ArgumentNullException(nameof(keys));

            var dict = new Dictionary<string, object>();
            keys.ToList().ForEach(item => dict.Add(item, Cache.Get(item)));
            return dict;
        }
        #endregion

        #region 删除缓存byKey
        /// <summary>
        /// 删除缓存byKey
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public static void Remove(string key)
        {
            if (key == null)
                throw new ArgumentNullException(nameof(key));

            Cache.Remove(key);
        }

        /// <summary>
        /// 批量删除缓存
        /// </summary>
        /// <returns></returns>
        public static void RemoveAll(IEnumerable<string> keys)
        {
            if (keys == null)
                throw new ArgumentNullException(nameof(keys));

            keys.ToList().ForEach(item => Cache.Remove(item));
        }
        #endregion

        #region 移除指定数据缓存, 模糊匹配包含某个关键字的缓存
        /// <summary>
        /// 移除指定数据缓存, 模糊匹配包含某个关键字的缓存
        /// </summary>
        /// <param name="CacheKey"></param>
        public static void RemoveAllCacheContains(string CacheKey)
        {
            var cacheKeys = GetCacheKeys();
            var _list = cacheKeys.Where(k => k.Contains(CacheKey)).ToList();
            foreach (var item in _list)
            {
                Remove(item);
            }
        }
        #endregion

        #region 删除匹配到的缓存
        /// <summary>
        /// 删除匹配到的缓存
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public static void RemoveCacheRegex(string pattern)
        {
            IList<string> l = SearchCacheRegex(pattern);
            foreach (var s in l)
            {
                Remove(s);
            }
        }
        #endregion

        #region 搜索 匹配到的缓存
        /// <summary>
        /// 搜索 匹配到的缓存
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public static IList<string> SearchCacheRegex(string pattern)
        {
            var cacheKeys = GetCacheKeys();
            var l = cacheKeys.Where(k => Regex.IsMatch(k, pattern)).ToList();
            return l.AsReadOnly();
        }
        #endregion

        #region 获取所有缓存键
        /// <summary>
        /// 获取所有缓存键
        /// </summary>
        /// <returns></returns>
        public static List<string> GetCacheKeys()
        {
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var entries = Cache.GetType().GetField("_entries", flags).GetValue(Cache);
            var cacheItems = entries as IDictionary;
            var keys = new List<string>();
            if (cacheItems == null) return keys;
            foreach (DictionaryEntry cacheItem in cacheItems)
            {
                keys.Add(cacheItem.Key.ToString());
            }
            return keys;
        }
        #endregion
    }
}